﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Cubicle.Core.DataContracts;
using Cubicle.Core.Shared;
using Cubicle.Excel.Hosting;
using ExcelDna.Integration;

namespace Cubicle.Excel.AddIn
{
    [Export]
    public class ContextMenuFactory
    {
        // Nlog
        private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        public ContextMenuFactory()
        {
            var logAsm = new[] { "Print Assemblies", "LogAssemblies" };
            XlCall.Excel(XlCall.xlfAddCommand, 7, 4, logAsm, 0);
            var logType = new[] { "Print Type", "LogType" };
            XlCall.Excel(XlCall.xlfAddCommand, 7, 4, logType, 0);
            var logProp = new[] { "Print Properties", "LogProperties" };
            XlCall.Excel(XlCall.xlfAddCommand, 7, 4, logProp, 0);
            var logField = new[] { "Print Fields", "LogFields" };
            XlCall.Excel(XlCall.xlfAddCommand, 7, 4, logField, 0);
            var logMeth = new[] { "Print Methods", "LogMethods" };
            XlCall.Excel(XlCall.xlfAddCommand, 7, 4, logMeth, 0);
        }

        [ExcelCommand(IsHidden = true)]
        public static void LogProperties()
        {
            ActionWrapper((client, handle) => client.GetProperties(handle), PrintProperties);
        }

        [ExcelCommand(IsHidden = true)]
        public static void LogFields()
        {
            ActionWrapper((client, handle) => client.GetFields(handle), PrintFields);
        }

        [ExcelCommand(IsHidden = true)]
        public static void LogMethods()
        {
            ActionWrapper((client, handle) => client.GetMethods(handle), PrintMethods);
        }

        [ExcelCommand(IsHidden = true)]
        public static void LogAssemblies()
        {
            ActionWrapper((client, handle) => client.GetAssemblyDetails(), PrintAssemblies);
        }

        [ExcelCommand(IsHidden = true)]
        public static void LogType()
        {
            ActionWrapper((client, handle) => new List<TypeData> { client.GetType(handle) }, PrintType);
        }

        private static void PrintType(IEnumerable<TypeData> data)
        {
            data.Iter(p => _logger.Info("{0}", p.FullName));
        }

        private static void PrintProperties(IEnumerable<PropertyData> data)
        {
            data.Iter(p => _logger.Info("{0} {1}", p.PropertyType, p.PropertyName));
        }

        private static void PrintFields(IEnumerable<FieldData> data)
        {
            data.Iter(p => _logger.Info("{0} {1}", p.FieldType, p.FieldName));
        }

        private static void PrintMethods(IEnumerable<MethodData> data)
        {
            foreach (var item in data)
            {
                var signature = item.ReturnType + " " + item.Name + "(";
                var pData = Get((client, handle) => client.GetParameterData(handle, item.Name, item.Offset));
                foreach (var p in pData)
                    signature += p.ParameterType + " " + p.Name + ", ";
                if (pData.Any()) signature = signature.Remove(signature.Length - 2, 2);
                signature += ")";
                _logger.Info(signature);
            }
        }

        private static void PrintAssemblies(IEnumerable<AssemblyData> data)
        {
            data.Iter(p => _logger.Info("{0} {1} {2}", p.FullName, p.ProcessorArchitecture, p.Location));
        }

        private static T Get<T>(Func<EngineProxy, string, T> fGet)
        {
            var selection = (ExcelReference)XlCall.Excel(XlCall.xlfSelection);
            var client = CubicleAddIn.Engines.DefaultClient;
            var handle = selection.GetValue().ToString();
            return fGet(client, handle);
        }

        private static void ActionWrapper<T>(Func<EngineProxy, string, IEnumerable<T>> fGet, Action<IEnumerable<T>> act)
        {
            var data = Get(fGet);
            if (data == null || !data.Any())
            {
                _logger.Error("No data");
                return;
            }
            act(data);
        }

    }
}